namespace Angelus.Oracle.Entity.Common.Entity
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common.CommandTrees;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;

    /// <summary>
    /// This class is like StringBuilder. While traversing the treee for the first time,
    /// we do not know all the strings that need to be appened e.g. things that need to be
    /// renamed, nested select statements etc. So, we use a builder than can collect
    /// all kinds of query fragments.
    /// </summary>
    internal sealed class SqlBuilder : ISqlFragment
    {
        #region Fields

        private List<object> _fragments;

        #endregion

        #region Properties

        /// <summary>
        /// Whether the builder is empty.
        /// </summary>
        /// <remarks>
        /// This is used by the <see cref="SqlGenerator.Visit(DbProjectExpression)"/>
        /// to determine whether an oracle statement can be reused.
        /// </remarks>
        internal bool IsEmpty
        {
            get { return null == this._fragments || 0 == this._fragments.Count; }
        }

        /// <summary>
        /// 
        /// </summary>
        private List<object> QueryFragments
        {
            get
            {
                if (null == this._fragments)
                {
                    this._fragments = new List<object>();
                }

                return this._fragments;
            }
        }

        #endregion

        #region ISqlFragment Members

        /// <summary>
        /// We delegate the writing of the fragment to the appropriate type.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="generator"></param>
        public void WriteSql(SqlWriter writer, SqlGenerator generator)
        {
            this.QueryFragments.ForEach(delegate(object item)
                                            {
                                                string value = (item as string);
                                                if (null != value)
                                                {
                                                    writer.Write(value);
                                                }
                                                else
                                                {
                                                    ISqlFragment fragment = (item as ISqlFragment);
                                                    if (null == fragment)
                                                    {
                                                        throw new InvalidOperationException();

                                                    }

                                                    fragment.WriteSql(writer, generator);
                                                }
                                            });
        }

        #endregion

        #region Builder Facilities

        /// <summary>
        /// Add an object to the list.
        /// </summary>
        /// <param name="fragment"></param>
        /// <remarks>
        /// We do not verify that it is a proper query fragment
        /// since this is an internal method.
        /// </remarks>
        internal void Append(object fragment)
        {
            Contract.Assert(null != fragment);
            this.QueryFragments.Add(fragment);
        }

        /// <summary>
        ///  This is to pretty print the SQL. The write <see cref="SqlWriter.Write(string)" />
        /// needs to know about new lines so that it can add the right amount of
        /// indentation at the beginning of lines.
        /// </summary>
        internal void AppendLine()
        {
            this.QueryFragments.Add(Environment.NewLine);
        }

        #endregion
    }
}